Declaration Merging
선언 병합이란 컴파일러가 같은 이름으로 선언된 두개를 하나의 정의로 합치는 것을 말한다.
선언 타입 | 네임스페이스 | 타입 | 값 |
---|
네임스페이스 | X | | X |
클래스 | | X | X |
열거형 | | X | X |
인터페이스 | | X | |
타입 alias | | X | |
함수 | | | X |
변수 | | | X |
Interface merge
- 두 선언의 멤버들을 같은 이름의 단일 인터페이스로 결합한다.
- 같은 이름의 멤버가 다른 타입을 가지고 있으면 error가 발생한다.
- 이후에 선언된 인터페이스가 높은 우선순위를 가진다.
1interface Cloner {
2 clone(animal: Animal): Animal;
3}
4interface Cloner {
5 clone(animal: Sheep): Sheep;
6}
7interface Cloner {
8 clone(animal: Dog): Dog;
9 clone(animal: Cat): Cat;
10}
1interface Cloner {
2 clone(animal: Dog): Dog;
3 clone(animal: Cat): Cat;
4 clone(animal: Sheep): Sheep;
5 clone(animal: Animal): Animal;
6}
namespace merge
- 네임스페이스를 병합하기 위해서, 각 네임스페이스에 선언된 export 된 인터페이스로부터 타입 정의가 병합되며, 내부에 병합된 인터페이스 정의가 있는 단일 네임스페이스가 형성된다.
- 네임스페이스가 네임스페이스와 값 둘 다 만든다.
1namespace Animals {
2 export class Zebra {}
3}
4namespace Animals {
5 export interface Legged {
6 numberOfLegs: number;
7 }
8 export class Dog {}
9}
1namespace Animals {
2 export interface Legged {
3 numberOfLegs: number;
4 }
5 export class Zebra {}
6 export class Dog {}
7}
export하지 않는 멤버를 내부에서 사용하는 경우 주의점
1namespace Animal {
2 let haveMuscles = true;
3 export function animalsHaveMuscles() {
4 return haveMuscles;
5 }
6}
7namespace Animal {
8 export function doAnimalsHaveMuscles() {
9 return haveMuscles; // 오류, haveMuscles가 여기에 접근할 수 없기 때문에
10 }
11}
Class, Function, enum과 namespace의 merge
namespace는 다른 타입의 유형과 병합이 가능할 정도로 유연하다.
패턴과 같다. typescript는 선언 병합을 통해서 타입을 안전하게 보존하며 정의할 수 있다.
merging namespace with class
1class Album {
2 label: Album.AlbumLabel;
3}
4namespace Album {
5 export class AlbumLabel {}
6}
다른 클래스 내에서 관리되는 클래스. namespace를 사용해서 기존 클래스에 정적 멤버를 추가할 수 있다.
merging namespace with function
1function buildLabel(name: string): string {
2 return buildLabel.prefix + name + buildLabel.suffix;
3}
4namespace buildLabel {
5 export let suffix = "";
6 export let prefix = "Hello, ";
7}
8console.log(buildLabel("Sam Smith"));
merging namespace with enum
1enum Color {
2 red = 1,
3 green = 2,
4 blue = 4,
5}
6namespace Color {
7 export function mixColor(colorName: string) {
8 if (colorName == "yellow") {
9 return Color.red + Color.green;
10 } else if (colorName == "white") {
11 return Color.red + Color.green + Color.blue;
12 } else if (colorName == "magenta") {
13 return Color.red + Color.blue;
14 } else if (colorName == "cyan") {
15 return Color.green + Color.blue;
16 }
17 }
18}
모듈 보강
1// observable.ts
2export class Observable<T> {
3 // ... 연습을 위해 남겨둠 ...
4}
5// map.ts
6import { Observable } from "./observable";
7
8declare module "./observable" {
9 interface Observable<T> {
10 map<U>(f: (x: T) => U): Observable<U>;
11 }
12}
13
14Observable.prototype.map = function (f) {
15 // ... 연습을 위해 남겨둠
16};
17
18// consumer.ts
19import { Observable } from "./observable";
20import "./map";
21
22let o: Observable<number>;
23o.map((x) => x.toFixed());
위 상황은 Observable을 import해서 prototype에 map을 추가했을 때 consumer에서 type을 알 수 없다.
따라서 Observable을 merge를 통해서 map을 보강해준다.
전역 보강
1// observable.ts
2export class Observable<T> {
3 // ... 연습을 위해 남겨둠 ...
4}
5declare global {
6 interface Array<T> {
7 toObservable(): Observable<T>;
8 }
9}
10Array.prototype.toObservable = function () {
11 // ...
12};
모듈 내부에서 전역 스코프에 선언을 추가해줄 수 있다.
disallowed merges
클래스는 다른 클래스나 변수와 병합할 수 없다.